package com.zy.cat.common.aspect;

import java.lang.reflect.Method;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;

import com.zy.cat.common.annotation.CheckingMD5;
import com.zy.cat.entity.resp.RSPE;
import com.zy.cat.service.CacheService;

import lombok.extern.slf4j.Slf4j;

@Aspect
@Component
@Slf4j
public class CheckingMD5Aspect {

	@Autowired
	@Qualifier("redis16")
	RedisTemplate<String, String> redis16;

	@Autowired
	@Qualifier("redis1")
	RedisTemplate<String, Long> redis1;

	@Autowired
	CacheService cacheService;

	private static final Set<String> SINGLEKEY = new ConcurrentSkipListSet<>();

	@Around("@annotation(checkingMD5)")
	public Object checkingMD5(ProceedingJoinPoint joinPoint, CheckingMD5 checkingMD5) {

		String md5Key = "token";
		if (StringUtils.isNotBlank(checkingMD5.MD5())) {
			md5Key = checkingMD5.MD5();
		}

		// 获取传入目标方法的参数
		String md5Str = parseKey(joinPoint, checkingMD5.checkingStr(), "md5Str");
		String md5 = parseKey(joinPoint, md5Key, "token");
		String uuid = parseKey(joinPoint, "uuid", "");
		String rtypeTemp = parseKey(joinPoint, "rtype", "");

		int rtype = 0;

		if (StringUtils.isNumeric(rtypeTemp)) {
			rtype = Integer.parseInt(rtypeTemp);
		}
		log.info(rtypeTemp + "-rtype-->" + rtype);
		if (StringUtils.isBlank(md5Str) || StringUtils.isBlank(md5) || StringUtils.isBlank(uuid)) {
			log.info("rtype-->" + rtype);
			log.info("md5Str-->" + md5Str);
			log.info("md5-->" + md5);
			log.info("uuid-->" + uuid);
			return new RSPE<Object>(-1, "认证失败");
		}

//		md5Str += "_" + LocalDate.now().toString();
		/* 安卓增加时区认证，IOS去掉时区认证 */
		if (rtype == 0) {
			md5Str += "_" + LocalDate.now().toString();
		}
		String md5Comper = DigestUtils.md5DigestAsHex(md5Str.getBytes());

		if (md5.equals(md5Comper)) {

			/* 鉴权接口限制uuid每次必须不一样 */
			if (StringUtils.isNotBlank(uuid)) {
				String key = "CAT_REQUEST_UUID_LIMIT_" + uuid;
				if (redis1.hasKey(key)) {
					long rtimes = redis1.opsForValue().increment(key, 1);
					if (rtimes > 10) {
						redis1.expire(key, 1, TimeUnit.HOURS);
					}
					return new RSPE<Object>(-6666, "请求频率异常，请稍后重试！");
				} else {
					redis1.opsForValue().set(key, 1L, 5, TimeUnit.MINUTES);
				}
			}
			String sign = "";
			boolean checkSingle = checkingMD5.checkSingle() && StringUtils.isNotBlank(checkingMD5.singleStr());
			if (checkSingle) {
//				String singleStr = parseKey(joinPoint, checkingMD5.singleStr(), "singleStr");
//				String method = joinPoint.getSignature().getDeclaringTypeName() + joinPoint.getSignature().getName();
//				// 拼接签名
//				sign = method + "_" + singleStr;
//				boolean success = SINGLEKEY.add(sign);
//				if (!success) {
//					log.error(method + "-->正在执行,不能重复请求->" + SINGLEKEY.size());
//					return new RSPE<Object>(-6666, "请求频率异常，请稍后重试！");
//				}
			}

			String method = joinPoint.getSignature().getName();
			try {
				Object obj = joinPoint.proceed();
				return obj;
			} catch (Throwable e) {
				log.error("-认证失败-发生异常-->{},method:{},key:{}", e.getLocalizedMessage(), method, md5Str);
				return new RSPE<Object>(-9, "发生异常");
			} finally {
				if (checkSingle) {
					SINGLEKEY.remove(sign);
				}
			}
		}
		log.info("认证失败--->rtype-->" + rtype);
		log.info("认证失败--->md5Str-->" + md5Str);
		log.info("认证失败--->md5Comper-->" + md5Comper);
		log.info("认证失败--->md5-->" + md5);
		return new RSPE<Object>(-1, "认证失败");
	}

	/**
	 * @info 获取缓存的key对应的值
	 * @author Link
	 * @date 2019-07-05 14:18
	 * @param joinPoint
	 * @param spel
	 * @param defaultValuue
	 * @return
	 */
	private String parseKey(ProceedingJoinPoint joinPoint, String spel, String defaultValuue) {
		String result = "";
		// 获取参数的类型
		Method method = null;
		Object[] args = joinPoint.getArgs();
		spel = spel.replaceAll("\\s*", "");
		try {
			MethodSignature signature = (MethodSignature) joinPoint.getSignature();
			method = signature.getMethod();
		} catch (SecurityException e) {
			e.printStackTrace();
			return defaultValuue;
		}

		Map<String, Object> map = new HashMap<String, Object>();

		// 获取被拦截方法参数名列表(使用Spring支持类库)
		LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
		String[] paraNameArr = u.getParameterNames(method);

		// 把方法参数放入SPEL上下文中
		for (int i = 0; i < paraNameArr.length; i++) {
//			log.info(paraNameArr[i]+"<------->"+args[i]);
			if (args[i] == null) {
				map.put(paraNameArr[i], "");
			} else {
				map.put(paraNameArr[i], args[i]);
			}

		}
		for (String str : spel.split("_")) {
			String val = "";
			if (map.get(str) != null) {
				val = map.get(str).toString();
			}

			if (StringUtils.isBlank(result)) {
				result = val;
			} else {
				result += "_" + val;
			}
		}
		return result;
	}
}
